package com.heima.article.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.heima.article.dto.ArticleStreamMessage;
import com.heima.article.entity.ApArticle;
import com.heima.article.service.IApArticleService;
import com.heima.article.service.IHotArticleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @Author WSJ
 * @Date 2022-03-23-20:51
 * @Version 1.0 （版本号）
 */
@Service
public class HotArticleServiceImpl implements IHotArticleService {
    @Autowired
    private IApArticleService articleService;
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Override
    public void compute() {
        //每日凌晨1点，计算前五天的文章热门分数，缓存到redis中，并优先展示在APP主界面和各个频道
        //当天零点
        Date now = new Date();
        Date to = new Date(now.getYear(), now.getMonth(), now.getDate());
        //前五天零点
        Date from = new Date(to.getTime() - 24 * 60 * 60 * 1000 * 5);
        //获取五天内的文章
        LambdaQueryWrapper<ApArticle> wrapper = new LambdaQueryWrapper<>();
        wrapper.ge(ApArticle::getPublishTime,from);
        wrapper.le(ApArticle::getPublishTime, to);
        //排除下架删除的
        wrapper.eq(ApArticle::getIsDelete, false);
        wrapper.eq(ApArticle::getIsDown, false);
        //查询
        List<ApArticle> apArticles = articleService.list(wrapper);
        //计算分值，并存入redis
        if (!CollectionUtils.isEmpty(apArticles)) {
            for (ApArticle apArticle : apArticles) {
                //计算分值
                double score=computeScore(apArticle);
                //存入redis
                String key="hotArticle_";
                //设置变化值为空
                apArticle.setCollection(null);
                apArticle.setComment(null);
                apArticle.setLikes(null);
                apArticle.setViews(null);
                String json = JSON.toJSONString(apArticle);
                //主界面
                redisTemplate.opsForZSet().add(key+0,json ,score);
                redisTemplate.expire(key + 0, 24, TimeUnit.HOURS);
                //其他频道
                redisTemplate.opsForZSet().add(key + apArticle.getChannelId(), json, score);
                redisTemplate.expire(key + apArticle.getChannelId(), 24, TimeUnit.HOURS);
            }
        }
    }

    @Override
    public void update(ArticleStreamMessage message) {
        //计算文章的分值
        //根据文章id获取文章
        ApArticle article = articleService.getById(message.getArticleId());
        //计算新增文章分数
        double scorePlus=computeScorePlus(message);
        //判断缓存中是否有当前文章id
        ApArticle apArticle = article;
        String key="hotArticle_";
        apArticle.setCollection(null);
        apArticle.setComment(null);
        apArticle.setLikes(null);
        apArticle.setViews(null);
        String json = JSON.toJSONString(apArticle);
        Double score = redisTemplate.opsForZSet().score(key + 0, json);
        Double scoreByChannelId = redisTemplate.opsForZSet().score(key + apArticle.getChannelId(), json);
        if (score!=null) {
            //如果主页面缓存中已存在当前文章，则更新score
            redisTemplate.opsForZSet().add(key+0,json,score+scorePlus);
        }else {
            // 如果不在redis中,需要先计算之前的分值,再加上增量的分值,再添加数据到redis中
            double computeScore = computeScore(article);
            redisTemplate.opsForZSet().add(key + 0, json, computeScore + scorePlus);
        }
        if (scoreByChannelId!=null) {
            //如果当前文章所在频道缓存中已存在当前文章，则更新score
            redisTemplate.opsForZSet().add(key+apArticle.getChannelId(),json,score+scorePlus);
        }else {
            // 如果不在redis中,需要先计算之前的分值,再加上增量的分值,再添加数据到redis中
            double computeScore = computeScore(article);
            redisTemplate.opsForZSet().add(key + apArticle.getChannelId(), json, computeScore + scorePlus);
        }
        //需要将本次新增数据写入到数据库中
        LambdaUpdateWrapper<ApArticle> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(ApArticle::getId,article.getId());
        updateWrapper.setSql("likes=likes+"+message.getLike());
        updateWrapper.setSql("collection=collection+" + message.getCollect());
        updateWrapper.setSql("comment=comment+"+message.getComment());
        updateWrapper.setSql("views=views+"+message.getView());
        articleService.update(updateWrapper);
    }

    /**
     * 计算较上一次新增分数，3倍权重
     * @param message
     * @return
     */
    private double computeScorePlus(ArticleStreamMessage message) {
        double score=0;
        score+=message.getView()*1*3;
        score+=message.getLike()*3*3;
        score+=message.getComment()*5*3;
        score += message.getCollect() * 8 * 3;
        return score;
    }

    /**
     *  计算分数
     * @param apArticle
     * @return
     */
    private double computeScore(ApArticle apArticle) {

        double score=0;
        // 阅读权重 1
        if (apArticle.getViews()!=null) {
            score+=apArticle.getViews();
        }
        // 点赞权重 3
        if (apArticle.getLikes()!=null) {
            score+=apArticle.getLikes()*3;
        }
        // 评论权重 5
        if (apArticle.getComment()!=null) {
            score+=apArticle.getComment()*5;
        }
        // 收藏权重 8
        if (apArticle.getCollection()!=null) {
            score += apArticle.getCollection() * 8;
        }
        return score;

    }
}
